using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Collections.Specialized;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;

namespace ApplicationTemplate
{
    /// <summary>
    /// DAMのスーパークラスです
    /// </summary>
    [Serializable()]
    public class BaseDAM
    {
        /// <summary>
        /// クエリー実行時のモード判別用enum
        /// </summary>
        public enum QueryMode
        {
            /// <summary>
            /// インサー�?
            /// </summary>
            insert,

            /// <summary>
            /// アップデート
            /// </summary>
            update
        }

        /// <summary>
        /// LIKE検索モー�?
        /// </summary>
        public enum LikeMode
        {
            /// <summary>
            /// 前方一�?
            /// </summary>
            prefix_search,

            /// <summary>
            /// 後方一�?
            /// </summary>
            suffix_search,

            /// <summary>
            /// 部分一�?
            /// </summary>
            partial_search
        }

        /// <summary>
        /// EnterpriseLibraryのDBオブジェクト
        /// </summary>
        [NonSerialized()]
        private SqlDatabase _db = null;
        /// <summary>
        /// EnterpriseLibraryのDBオブジェクト
        /// </summary>
        public SqlDatabase Db
        {
            set
            {
                ExceptionIsConnected();
                _db = value;
            }
        }


        private int _timeout = 1000;

        public BaseDAM(String serverName, String dbName, String userid, String password)
        {
            CreateDataBase(serverName, dbName, userid, password);
        }

        /// <summary>
        /// 初期化の時データベースオブジェクトを作成する�?
        /// </summary>
        /// <param name="dbName">DB名称</param>
        public void CreateDataBase(String serverName, String dbName, String userid, String password)
        {
            ExceptionIsConnected();
            String connString = String.Format(@"Database={0};Server={1};uid={2};pwd={3};Integrated Security=false;",
                dbName, serverName, userid, password);

            _db = new SqlDatabase(connString);
        }

        /// <summary>
        /// すでに_dbが生成されていたら、例外を発生させる�?
        /// </summary>
        private void ExceptionIsConnected()
        {
            if (_db != null)
                throw new Exception("すでに接続が確立されています?");
        }

        /// <summary>
        /// 内部的にデータベースオブジェクトを作成する�?
        /// </summary>
        /// <param name="ConnectionString">接続文字�?/param>
        public virtual void CreateDatabaseByConnectionString(String ConnectionString)
        {
            throw new Exception("DataBase毎にデータベース接続処理を実装する必要あります?");
        }

        /// <summary>
        /// データベースコマンドを得�?
        /// </summary>
        /// <param name="CommandString">接続文字�?/param>
        /// <returns>データベースコマンド</returns>
        public DbCommand GetSqlStringCommand(String CommandString)
        {
            DbCommand command = _db.GetSqlStringCommand(CommandString);
            command.CommandTimeout = this._timeout;

            return command;
        }

        public DbCommand GetStoredProcCommand(String spName)
        {
            DbCommand command = _db.GetStoredProcCommand(spName);
            command.CommandTimeout = this._timeout;

            return command;
        }

        public DbCommand GetStoredProcCommand(String spName, Object[] paramValues)
        {
            DbCommand command = _db.GetStoredProcCommand(spName, paramValues);
            command.CommandTimeout = this._timeout;

            return command;
        }

        /// <summary>
        /// パラメータセット�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        public void AddInParameter(DbCommand command, String name, DbType dbType)
        {
            _db.AddInParameter(command, name, dbType);
        }

        /// <summary>
        /// パラメータセット�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        /// <param name="value">�?/param>
        public void AddInParameter(DbCommand command, String name, DbType dbType, object value)
        {
            _db.AddInParameter(command, name, dbType, value);
        }

        /// <summary>
        /// パラメータセット�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        /// <param name="sourceColumn">カラム名（id�?/param>
        /// <param name="sourceVersion">ソースバージョン</param>
        public void AddInParameter(DbCommand command, String name, DbType dbType, string sourceColumn, DataRowVersion sourceVersion)
        {
            _db.AddInParameter(command, name, dbType, sourceColumn, sourceVersion);
        }

        /// <summary>
        /// パラメータセット（IN句用�?br />
        /// SetPluralConditionStringsメソッドとセットとなりま�?
        /// </summary>
        /// <param name="command">コマンドオブジェクト</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        /// <param name="values">パラメータ配列（1,2,3...�?/param>
        public void AddInParameter(DbCommand command, String name, DbType dbType, object[] values)
        {
            // 条件が未指定の場合は、スキップす�?
            if (values == null)
                return;

            int list_count = values.Length;
            for (int i = 0; i < list_count; i++)
            {
                _db.AddInParameter(command, name + "_" + i.ToString(), dbType, values[i]);
            }
        }

        public void AddStoreProcedureReturnParameter(DbCommand command, String name, DbType dbType)
        {
            _db.AddParameter(command, name, dbType, ParameterDirection.ReturnValue, "", DataRowVersion.Current, null);

        }

        /// <summary>
        /// パラメータセット（LIKE句）
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        /// <param name="mode">LIKE検索モー�?/param>
        /// <param name="value">�?/param>
        public void AddInParameterForLike(DbCommand command, String name, DbType dbType, LikeMode mode, object value)
        {
            // 値の無害�?
            string likeValue = SafeSqlLikeClauseLiteral(value);
            switch (mode)
            {
                // 前方一�?
                case LikeMode.prefix_search:
                    likeValue = likeValue + "%";
                    break;
                // 後方一�?
                case LikeMode.suffix_search:
                    likeValue = "%" + likeValue;
                    break;
                // 部分一�?
                case LikeMode.partial_search:
                    likeValue = "%" + likeValue + "%";
                    break;
            }
            _db.AddInParameter(command, name, dbType, likeValue);
        }


        /// <summary>
        /// 複数条件の設�?br />
        /// パラメータ文字列<br />
        ///  ・{sourceColumn} IN ({name}_0, {name}_1,....)
        /// </summary>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="sourceColumn">カラム名（id�?/param>
        /// <param name="values">パラメータ配列（1,2,3...�?/param>
        /// <returns>パラメータ文字列</returns>
        public String SetPluralConditionStrings(String name, string sourceColumn, object[] values)
        {
            return ComposePluralConditionStrings(false, name, sourceColumn, values);
        }

        /// <summary>
        /// 複数条件（NOT）の設定<br />
        /// パラメータ文字列<br />
        ///  ・{sourceColumn} NOT IN ({name}_0, {name}_1,....)
        /// </summary>
        /// <param name="name"></param>
        /// <param name="sourceColumn"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public String SetPluralConditionStringsNot(String name, string sourceColumn, object[] values)
        {
            return ComposePluralConditionStrings(true, name, sourceColumn, values);
        }

        /// <summary>
        /// 複数条件の設�?
        /// </summary>
        /// <param name="IsNot"></param>
        /// <param name="name"></param>
        /// <param name="sourceColumn"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        private String ComposePluralConditionStrings(bool IsNot, String name, string sourceColumn, object[] values)
        {
            // 条件がNULLの場合は、空白を返す
            if (values == null)
                return "";

            int list_count = values.Length;
            // 条件が未指定の場合は、空白を返す
            if (list_count == 0)
                return "";


            StringBuilder paramerterStr = new StringBuilder();
            String paramFormat = "";    // Oracleの場合は、「OR」文に切り替える処理を将来的に埋め込めむ事も可能
            if (IsNot)
                paramFormat = "{0} NOT IN({1}) ";
            else
                paramFormat = "{0} IN({1}) ";


            // 指定されたパラメータ配列数分、パラメータ文字列を連番を付与して作成す�?
            for (int i = 0; i < list_count; i++)
            {
                paramerterStr.Append(name).Append("_").Append(i).Append(",");
            }

            // 余分なカンマを削除す�?
            paramerterStr = paramerterStr.Remove(paramerterStr.Length - 1, 1);

            // パラメータ文字列を返�?
            return String.Format(paramFormat, sourceColumn, paramerterStr.ToString());

        }

        /// <summary>
        /// LIKE句入力パラメータの無害化
        /// </summary>
        /// <param name="value">�?/param>
        /// <returns>無害化した�?/returns>
        public virtual string SafeSqlLikeClauseLiteral(object value)
        {
            throw new Exception("DataBase毎に実装が必要です?");
        }


        /// <summary>
        /// パラメータセット（出力用�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <param name="dbType">データ型</param>
        /// <param name="size">サイ�?/param>
        public void AddOutParameter(DbCommand command, String name, DbType dbType, int size)
        {
            _db.AddOutParameter(command, name, dbType, size);
        }

        /// <summary>
        /// パラメータ値取�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="name">パラメータ名（@id�?/param>
        /// <returns>取得�?/returns>
        public object GetParameterValue(DbCommand command, String name)
        {
            return _db.GetParameterValue(command, name);
        }

        /// <summary>
        /// データセットを得�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <returns>数据集合</returns>
        public DataSet ExecuteDataSet(DbCommand command)
        {
            //// 実行前にログを取っておく
            //Log(command);
            return _db.ExecuteDataSet(command);
        }

        /// <summary>
        /// データセットを得�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="manager">データベースマネージャ</param>
        /// <returns>数据集合</returns>
        public DataSet ExecuteDataSet(DbCommand command, DBManager manager)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteDataSet(command);

            //// 実行前にログを取っておく
            //LogTransaction(manager, command);
            return _db.ExecuteDataSet(command, manager.Transaction);
        }

        /// <summary>
        /// 結果セットの必要のないクエリを、実行する�?br />
        /// Transaction制御なし
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        public int ExecuteNonQuery(DbCommand command)
        {
            //// 実行前にログを取っておく
            //Log(command);
            return _db.ExecuteNonQuery(command);
        }

        /// <summary>
        /// 結果セットの必要のないクエリを、実行する�?br />
        /// Transaction制御あり。引数にDbTransactionを渡した場合、DbConnectionオブジェクトが共有される�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="manager">データベースマネージャ</param>
        public int ExecuteNonQuery(DbCommand command, DBManager manager)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteNonQuery(command);

            // 実行前にログを取っておく
            //LogTransaction(manager, command);
            return _db.ExecuteNonQuery(command, manager.Transaction);
        }

        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(DbCommand command)
        {
            // 実行前にログを取っておく
            //Log(command);
            return _db.ExecuteScalar(command);
        }

        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="commandType">コマンドタイ�?/param>
        /// <param name="commandText">コマンドテキスト</param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(CommandType commandType, string commandText)
        {
            // 実行前にログを取っておく
            //LogCommandType(commandType, commandText);
            return _db.ExecuteScalar(commandType, commandText);
        }

        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="manager">データベースマネージャ</param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(DbCommand command, DBManager manager)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteScalar(command);

            // 実行前にログを取っておく
            //LogTransaction(manager, command);
            return _db.ExecuteScalar(command, manager.Transaction);
        }

        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="storedProcedureName">ストアドプロシージャ�?/param>
        /// <param name="parameterValues">パラメータ配列（1,2,3...�?/param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(string storedProcedureName, params object[] parameterValues)
        {
            // 実行前にログを取っておく
            //LogStoredProcedure(storedProcedureName, parameterValues);
            return _db.ExecuteScalar(storedProcedureName, parameterValues);
        }

        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="manager">データベースマネージャ</param>
        /// <param name="commandType">コマンドタイ�?/param>
        /// <param name="commandText">コマンドテキスト</param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(DBManager manager, CommandType commandType, string commandText)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteScalar(commandType, commandText);

            // 実行前にログを取っておく
            //LogCommandTypeTransaction(manager, commandType, commandText);
            return _db.ExecuteScalar(manager.Transaction, commandType, commandText);
        }


        /// <summary>
        /// 問い合わせ結果セットの最初の列の最初のカラムを戻す�?余分なカラム、列は無視される�?
        /// </summary>
        /// <param name="manager">データベースマネージャ</param>
        /// <param name="storedProcedureName">ストアドプロシージャ�?/param>
        /// <param name="parameterValues">パラメータ配列（1,2,3...�?/param>
        /// <returns>先頭カラムオブジェク�?/returns>
        public object ExecuteScalar(DBManager manager, string storedProcedureName, params object[] parameterValues)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteScalar(storedProcedureName, parameterValues);

            // 実行前にログを取っておく
            //LogStoredProcedureTransaction(manager, storedProcedureName, parameterValues);
            return _db.ExecuteScalar(manager.Transaction, storedProcedureName, parameterValues);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(DbCommand command)
        {
            // 実行前にログを取っておく
            //Log(command);
            return _db.ExecuteReader(command);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="commandType">コマンドタイ�?/param>
        /// <param name="commandText">コマンドテキスト</param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(CommandType commandType, string commandText)
        {
            // 実行前にログを取っておく
            //LogCommandType(commandType, commandText);
            return _db.ExecuteReader(commandType, commandText);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <param name="manager">データベースマネージャ</param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(DbCommand command, DBManager manager)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteReader(command);

            // 実行前にログを取っておく
            //LogTransaction(manager, command);
            return _db.ExecuteReader(command, manager.Transaction);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="storedProcedureName">ストアドプロシージャ�?/param>
        /// <param name="parameterValues">パラメータ配列（1,2,3...�?/param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(string storedProcedureName, params object[] parameterValues)
        {
            // 実行前にログを取っておく
            //LogStoredProcedure(storedProcedureName, parameterValues);
            return _db.ExecuteReader(storedProcedureName, parameterValues);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="manager">データベースマネージャ</param>
        /// <param name="commandType">コマンドタイ�?/param>
        /// <param name="commandText">コマンドテキスト</param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(DBManager manager, CommandType commandType, string commandText)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteReader(commandType, commandText);

            // 実行前にログを取っておく
            //LogCommandTypeTransaction(manager, commandType, commandText);
            return _db.ExecuteReader(manager.Transaction, commandType, commandText);
        }

        /// <summary>
        /// 最も軽い問い合わせを行�?
        /// </summary>
        /// <param name="manager">データベースマネージャ</param>
        /// <param name="storedProcedureName">ストアドプロシージャ�?/param>
        /// <param name="parameterValues">パラメータ配列（1,2,3...�?/param>
        /// <returns>データリーダーインターフェー�?/returns>
        public IDataReader ExecuteReader(DBManager manager, string storedProcedureName, params object[] parameterValues)
        {
            // データマネージャがNULLの場合、Transactionなしのメソッドを呼び出す
            if (manager == null)
                return this.ExecuteReader(storedProcedureName, parameterValues);

            // 実行前にログを取っておく
            //LogStoredProcedureTransaction(manager, storedProcedureName, parameterValues);
            return _db.ExecuteReader(manager.Transaction, storedProcedureName, parameterValues);
        }


        /// <summary>
        /// DAMトランザクション開始
        /// </summary>
        /// <returns>データベースマネージャ</returns>
        public DBManager BeginDAMTransaction()
        {
            // 実行前にログをとっておく�?

            DBManager manager = new DBManager(_db.CreateConnection());
            manager.BeginTransaction();

            return manager;
        }


        /// <summary>
        /// 分離レベルを指定してDAMトランザクション開始
        /// </summary>
        /// <param name="isolationLevel">トランザクションの分離レベル</param>
        /// <returns>データベースマネージャ</returns>
        public DBManager BeginDAMTransaction(IsolationLevel isolationLevel)
        {
            // 実行前にログをとっておく�?

            DBManager manager = new DBManager(_db.CreateConnection());
            manager.BeginTransaction(isolationLevel);

            return manager;
        }

        /// <summary>
        /// データセットが保持するテーブルの内容がすべて同一か比較する�?br />
        /// </summary>
        /// <param name="sourceDS">最新数据集�?/param>
        /// <param name="destDS">比較数据集合</param>
        /// <returns>[true] 変更あり [false] 変更なし</returns>
        public Boolean IsDataSetChanged(DataSet sourceDS, DataSet destDS)
        {
            Boolean HasNE = false;

            if (sourceDS == null || destDS == null || sourceDS.Tables.Count == 0 || destDS.Tables.Count == 0 || sourceDS.Tables.Count != destDS.Tables.Count)
            {
                //他ユーザに物理削�?
                HasNE = true;
                return HasNE;
            }

            //「レコードを物理削除」の場合
            if (destDS.Tables[0].Rows.Count > 0 && (sourceDS.Tables.Count == 0 || sourceDS.Tables[0].Rows.Count == 0))
            {
                //他ユーザに物理削�?
                HasNE = true;
                return HasNE;
            }


            for (int i = 0; i < sourceDS.Tables.Count; i++)
            {
                HasNE = IsRowsChanged(sourceDS.Tables[i], destDS.Tables[i]);

                if (HasNE)
                {
                    break;
                }
            }
            return HasNE;
        }

        /// <summary>
        /// 行の内容がすべて同一か比較する�?
        /// </summary>
        /// <param name="sourceDT">最新データテーブル</param>
        /// <param name="destDT">比較データテーブ�?/param>
        /// <returns>[true] 変更あり [false] 変更なし</returns>
        public Boolean IsRowsChanged(DataTable sourceDT, DataTable destDT)
        {
            Boolean HasNE = false;

            //件数が一致しない場合
            if (sourceDT.Rows.Count != destDT.Rows.Count)
            {
                return true;
            }

            int RowCount = sourceDT.Rows.Count;
            int ColumnCount = sourceDT.Columns.Count;

            //列数が一致しない場合
            if (sourceDT.Columns.Count != destDT.Columns.Count)
            {
                return true;
            }

            for (int i = 0; i < RowCount; i++)
            {
                HasNE = IsColumnsChanged(ColumnCount, sourceDT.Rows[i], destDT.Rows[i]);

                if (HasNE)
                {
                    break;
                }
            }
            return HasNE;
        }

        /// <summary>
        /// カラム内容がすべて同一か比較する�?
        /// </summary>
        /// <param name="count">レコード件数</param>
        /// <param name="sourceRow">最新データ�?/param>
        /// <param name="destRow">比較データ行</param>
        /// <param name="dsChangedModel">排他チェック用数据模�?/param>
        /// <returns>[true] 変更あり [false] 変更なし</returns>
        public Boolean IsColumnsChanged(int count, DataRow sourceRow, DataRow destRow)
        {
            Boolean HasNE = false;

            if (sourceRow.Table.Columns.Contains("record_status") && sourceRow["record_status"].ToString() == "9")
            {
                //他ユーザに論理削�?
                HasNE = true;
            }

            if (!HasNE)
            {
                for (int i = 0; i < count; i++)
                {
                    if (sourceRow[i].ToString() != destRow[i].ToString())
                    {
                        //他ユーザ変更の場�?
                        HasNE = true;
                        break;
                    }
                }
            }

            return HasNE;
        }

        /// <summary>
        /// 画面ブランク項目がDBNULLに変換す�?
        /// </summary>
        /// <param name="command">データベースコマンド</param>
        /// <returns>DbCommand</returns>
        public DbCommand SetEmpty2DBNull(DbCommand command)
        {
            if (command == null || command.Parameters.Count == 0)
            {
                return command;
            }
            foreach (DbParameter param in command.Parameters)
            {
                //文字列の場合  
                if ((param.Value is String) && String.IsNullOrEmpty((String)param.Value))
                {
                    param.Value = DBNull.Value;
                }
            }
            return command;
        }

        /// <summary>
        /// データベース情報の初期化
        /// </summary>
        /// <returns>データベース情報</returns>
        public virtual StringDictionary InitDataBaseInfo()
        {
            throw new Exception("DataBase毎にデータベース情報の初期化処理を実装する必要あります?");
        }

        public DataSet TableList
        {
            get
            {
                DbCommand command = GetSqlStringCommand("select name from sys.all_objects where [type_desc] ='USER_TABLE' order by name");

                return ExecuteDataSet(command);
            }
        }
    }
}
